const Workbook = require('../doc/workbook');
const _ = require('lodash');
const assert = require('assert');

class TemplateHelper {
  constructor(workbook) {
    this.workbook = workbook || new Workbook();
    this.sheetHelpers = Object.create(null);
    this.workbook.eachSheet(sheet => {
      const sheetHelper = new WorksheetTemplateHelper(sheet, this);
      this.sheetHelpers[sheet.name] = sheetHelper;
      this.sheetHelpers[sheet.id] = sheetHelper;
    });
  }

  add(name, option) {
    const sheet = this.workbook.addWorksheet(name, option);
    const sheetHelper = new WorksheetTemplateHelper(sheet, this);
    this.sheetHelpers[sheet.name] = sheetHelper;
    this.sheetHelpers[sheet.id] = sheetHelper;
    return sheetHelper;
  }

  get(id) {
    return this.sheetHelpers[id];
  }

  sheet(name, option) {
    if (arguments.length === 2) {
      return this.add(name, option);
    } else {
      return this.get(name) || this.add();
    }
  }

  finish(options) {
    const sheetTemplateOptions = [];
    this.workbook.worksheets.forEach((sheet) => {
      const option = _.get(options, ['sheets', sheet.id], _.get(options, ['sheets', sheet.name]));
      if (!option) {
        sheetTemplateOptions.push(options);
      } else {
        sheetTemplateOptions.push(option);
      }
    });

    return this.workbook.xlsx.toTemplate(sheetTemplateOptions);
  }
}

class WorksheetTemplateHelper {
  constructor(sheet, parent) {
    this.sheet = sheet;
    this.parent = parent;
  }

  finish() {
    return this.parent.finish(...arguments);
  }

  book() {
    return this.parent;
  }


  addRow(rowCount, cellTypes) {
    if (_.isEmpty(cellTypes)) {
      cellTypes = this.colTypes;
    }
    while (rowCount--) {
      this.sheet.addRow(_.castArray(cellTypes));
    }
    return this;
  }

  image(id, range) {
    if (typeof id === 'number') {
      this.sheet.addImage(id, range);
    } else {
      id = this.parent.workbook.addImage(id);
      this.sheet.addImage(id, range);
    }
    return this;
  }

  setColumns(cols, rowNo = 1) {
    this.colTypes = _.map(cols, 'type');
    this.sheet.setColumns(cols, rowNo);
    return this;
  }

  style(style, rowNo, colNo) {
    let cells = [];
    if (rowNo && colNo) {
      cells.push(this.sheet.getCell(rowNo, colNo));
    } else if (!rowNo) {
      this.sheet.rows.forEach(row => cells.push(row.getCell(colNo)));
    } else {
      cells = this.sheet.getRow(rowNo)._cells;
    }
    cells.forEach(cell => cell.style = style);
    return this;
  }

  row(rowNo, cb) {
    const rows = [];
    const { sheet } = this;
    // 数字直接取对应的行；数组表示多行；{ start, end }表示行号区间
    if (typeof rowNo === 'number') {
      rows.push(sheet.getRow(rowNo));
    } else if (Array.isArray(rowNo)) {
      rowNo.forEach(no => this.row(no, cb));
      return this;
    } else if (typeof rowNo.start === 'number' && typeof rowNo.end === 'number') {
      let start = rowNo.start, end = rowNo.end;
      const maxRowNo = sheet.lastRow.number;

      if (start <= 0) {
        start = maxRowNo + start + 1;
      }
      if (end <= 0) {
        end = maxRowNo + end + 1;
      }

      assert(start >= 1 && end <= 1048577, 'excel支持的行数区间为: 1 ~ 1048576');
      assert(start < end, 'start必须小于end');

      for (let i = start; i < end; i++) {
        rows.push(sheet.getRow(i));
      }
    } else {
      throw new TypeError('无法解析出行号，仅支持: number, number[], { start: number, end: number }格式');
    }
    rows.forEach(row => cb(row));
    return this;
  }

  merge(...cells) {
    this.sheet.mergeCells(...cells);
    return this;
  }
}

module.exports = TemplateHelper;
